/********************************************************************
	created:	2010/5/17   20:44
	filename: 	zpf_time.h
	author:		zxh
	purpose:	time
*********************************************************************/

#ifndef _HY_TIMEVALUE_H_
#define _HY_TIMEVALUE_H_

#ifdef _WIN32

#include <time.h>
#include <windows.h>
typedef unsigned int suseconds_t;
struct timespec 
{ 
	time_t  tv_sec;  /* seconds */ 
	long    tv_nsec;  /* nanoseconds */
}; 

#else // _LINUX
struct timeval {
	long    tv_sec;         /* seconds */
	long    tv_usec;        /* and microseconds */
};
#endif

#include "hy_if_then_else.h"

//namespace hy {

enum
{
	HY_ONE_SECOND_IN_MSECS = 1000,
	HY_ONE_SECOND_IN_USECS = 1000000,
	HY_ONE_SECOND_IN_NSECS = 1000000000
};


/**
* @class HY_timeval
*
* @brief 
*/
class hy_timeval
{
public:
	// Constant "0".
	static const hy_timeval zero()
	{
		static const hy_timeval zero;
		return zero;
	}

	 static const hy_timeval max_time()
	 {
		  static const hy_timeval max_time(INT_MAX);
		  return max_time;
	 }


	// Default Constructor.
	hy_timeval ()
	{
		set (0, 0);
	}

	// Copy Constructor.
	hy_timeval (const hy_timeval& tv)
	{
		set (tv.tv_.tv_sec, tv.tv_.tv_usec);
	}

	// Constructor
	explicit hy_timeval (time_t sec, suseconds_t usec = 0)
	{
		set (sec, usec);
	}

	// Construct the HY_timeval from a timeval.
	explicit hy_timeval (const timeval &tv)
	{
		set (tv);
	}

	// Construct the HY_timeval object from a timespec_t.
	explicit hy_timeval (const timespec &tv)
	{
		set (tv);
	}

#if defined (_WIN32)
	// Construct the HY_timeval object from a Win32 FILETIME
	explicit hy_timeval (const FILETIME &ft)
	{
		 this->set (ft);
	}
#endif



	// Initializes the HY_timeval from seconds and useconds.
	void set (time_t sec, suseconds_t usec)
	{
		this->tv_.tv_sec  = static_cast<long>(sec);
		this->tv_.tv_usec = usec;
#if __GNUC__
		if (__builtin_constant_p(sec) &&
			__builtin_constant_p(usec) &&
			(sec >= 0 && usec >= 0 && usec < 1000000))
			return ;
#endif
		this->normalize ();
	}

	// Initializes the HY_timeval from a timeval.
	void set (const timeval &t)
	{
		this->tv_.tv_sec = t.tv_sec;
		this->tv_.tv_usec = t.tv_usec;
	}

	// Initializes the HY_timeval object from a timespec_t.
	void set (const timespec &t)
	{
		this->tv_.tv_sec = static_cast<long>(t.tv_sec);
		this->tv_.tv_usec = t.tv_nsec;
	}

#if defined (_WIN32)
	// Initializes the HY_timeval object from a Win32 FILETIME
	void set (const FILETIME &file_time)
	{
		ULARGE_INTEGER _100ns;
		_100ns.LowPart = file_time.dwLowDateTime;
		_100ns.HighPart = file_time.dwHighDateTime;
		_100ns.QuadPart -= 0x19db1ded53e8000i64;

		// Convert 100ns units to seconds;
		this->tv_.tv_sec = (long) (_100ns.QuadPart / (10000 * 1000));
		// Convert remainder to microseconds;
		this->tv_.tv_usec = (suseconds_t) ((_100ns.QuadPart % (10000 * 1000)) / 10);
		this->normalize ();
	}
#endif

	// Returns the value of the object as a timespec_t.
	operator timespec () const
	{
		timespec tv;
		tv.tv_sec = this->tv_.tv_sec;
		tv.tv_nsec = this->tv_.tv_usec * 1000;
		return tv;
	}

	// Returns the value of the object as a timeval.
	operator timeval () const
	{
		 return this->tv_;
	}

	// Returns a pointer to the object as a timeval.
	operator const timeval *() const
	{
		return (const timeval *) &this->tv_;
	}

#if defined (_WIN32)
	operator FILETIME () const
	{
		FILETIME file_time;
		ULARGE_INTEGER _100ns;
		_100ns.QuadPart = (((DWORDLONG) this->tv_.tv_sec * (10000 * 1000) +
			this->tv_.tv_usec * 10) +
			0x19db1ded53e8000i64);
		file_time.dwLowDateTime = _100ns.LowPart;
		file_time.dwHighDateTime = _100ns.HighPart;
		return file_time;
	}
#endif

	// = The following are accessor/mutator methods.
	time_t sec (void) const
	{
		 return this->tv_.tv_sec;
	}

	void sec (time_t sec)
	{
		this->tv_.tv_sec = static_cast<long>(sec);
	}

	unsigned long hy_timeval::msec (void) const
	{
		return this->tv_.tv_sec * 1000 + this->tv_.tv_usec / 1000;
	}

	suseconds_t usec (void) const
	{
		return this->tv_.tv_usec;
	}

	void usec (suseconds_t usec)
	{
		 this->tv_.tv_usec = usec;
	}

	// Get current time value
	void now ()
	{
#if defined (_WIN32)
		FILETIME   tfile;
		::GetSystemTimeAsFileTime (&tfile);
		this->set (tfile);
#else // _LINUX
		struct timeval tv;
		::gettimeofday (&tv, 0);
		this->set (tv);
#endif
	}

	hy_timeval &operator = (const hy_timeval &tv)
	{
		this->sec (tv.sec ());
		this->usec (tv.usec ());
		return *this;
	}

	hy_timeval &operator = (time_t tv)
	{
		this->sec (tv);
		this->usec (0);
		return *this;
	}

	hy_timeval &operator += (const hy_timeval &tv)
	{
		this->sec (this->sec () + tv.sec ());
		this->usec (this->usec () + tv.usec ());
		this->normalize ();
		return *this;
	}

	hy_timeval &operator += (const time_t tv)
	{
		this->sec (this->sec () + tv);
		return *this;
	}

	hy_timeval &operator -= (const hy_timeval &tv)
	{
		this->sec (this->sec () - tv.sec ());
		this->usec (this->usec () - tv.usec ());
		this->normalize ();
		return *this;
	}

	hy_timeval &operator -= (const time_t tv)
	{
		this->sec (this->sec () - tv);
		return *this;
	}

	/**
	\brief Multiply the time value by the @a d factor.
	\note The result of the operator is valid for results from range
	< (ACE_INT32_MIN, -999999), (ACE_INT32_MAX, 999999) >. Result
	outside this range are saturated to a limit.
	*/
	hy_timeval &operator *= (double d)
	{
		// The floating type to be used in the computations.  It should be
		// large enough to hold a time_t.  We actually want a floating type
		// with enough digits in its mantissa to hold a time_t without
		// losing precision.  For example, if FLT_RADIX is 2 and
		// LDBL_MANT_DIG is 64, a long double has a 64 bit wide mantissa,
		// which would be sufficient to hold a 64 bit time_t value without
		// losing precision.
		//
		// For now we'll simply go with long double if it is larger than
		// time_t.  We're hosed if long double isn't large enough.
		typedef hy::if_then_else<(sizeof (double) > sizeof (time_t)),
			double,
			long double>::result_type float_type;

		float_type time_total =
			(this->sec ()
			+ static_cast<float_type> (this->usec ()) / HY_ONE_SECOND_IN_USECS) * d;

		// shall we saturate the result?
		static const float_type max_int =
			INT_MAX + 0.999999;
		static const float_type min_int =
			INT_MIN - 0.999999;

		if (time_total > max_int)
			time_total = max_int;
		if (time_total < min_int)
			time_total = min_int;

		const time_t time_sec = static_cast<time_t> (time_total);

		time_total -= time_sec;
		time_total *= HY_ONE_SECOND_IN_USECS;

		suseconds_t time_usec = static_cast<suseconds_t> (time_total);

		// round up the result to save the last usec
		if (time_usec > 0 && (time_total - time_usec) >= 0.5)
			++time_usec;
		else if (time_usec < 0 && (time_total - time_usec) <= -0.5)
			--time_usec;

		this->set (time_sec, time_usec);

		return *this;
	}

	/// Increment microseconds as postfix.
	/**
	*/
	hy_timeval operator++ (int)
	{
		hy_timeval tv (*this);
		++*this;
		return tv;
	}

	/// Increment microseconds as prefix.
	/**
	*/
	hy_timeval &operator++ (void)
	{
		this->usec (this->usec () + 1);
		this->normalize ();
		return *this;
	}

	/// Decrement microseconds as postfix.
	/**
	*/
	hy_timeval operator-- (int)
	{
		hy_timeval tv (*this);
		--*this;
		return tv;
	}

	/// Decrement microseconds as prefix.
	/**
	*/
	hy_timeval &operator-- (void)
	{
		this->usec (this->usec () - 1);
		this->normalize ();
		return *this;
	}

	friend hy_timeval operator + (const hy_timeval &tv1, 
		const hy_timeval &tv2);

	friend hy_timeval operator - (const hy_timeval &tv1, 
		const hy_timeval &tv2);

	friend bool operator < (const hy_timeval &tv1,
		const hy_timeval &tv2);

	friend bool operator > (const hy_timeval &tv1,
		const hy_timeval &tv2);

	friend bool operator <= (const hy_timeval &tv1,
		const hy_timeval &tv2);

	friend bool operator >= (const hy_timeval &tv1,
		const hy_timeval &tv2);

	friend bool operator == (const hy_timeval &tv1,
		const hy_timeval &tv2);

	friend bool operator != (const hy_timeval &tv1,
		const hy_timeval &tv2);
protected:
	// Put the timevalue into a canonical form.
	void normalize (void)
	{
		if (this->tv_.tv_usec >= HY_ONE_SECOND_IN_USECS)
		{
			/*! \todo This loop needs some optimization.
			*/
			do
			{
				++this->tv_.tv_sec;
				this->tv_.tv_usec -= HY_ONE_SECOND_IN_USECS;
			}
			while (this->tv_.tv_usec >= HY_ONE_SECOND_IN_USECS);
		}
		else if (this->tv_.tv_usec <= -HY_ONE_SECOND_IN_USECS)
		{
			/*! \todo This loop needs some optimization.
			*/
			do
			{
				--this->tv_.tv_sec;
				this->tv_.tv_usec += HY_ONE_SECOND_IN_USECS;
			}
			while (this->tv_.tv_usec <= -HY_ONE_SECOND_IN_USECS);
		}

		if (this->tv_.tv_sec >= 1 && this->tv_.tv_usec < 0)
		{
			--this->tv_.tv_sec;
			this->tv_.tv_usec += HY_ONE_SECOND_IN_USECS;
		}
	}
private:
	struct timeval tv_;
};

inline
bool operator > (const hy_timeval &tv1, const hy_timeval &tv2)
{
	if (tv1.sec () > tv2.sec ())
		return true;
	else if (tv1.sec () == tv2.sec ()
		&& tv1.usec () > tv2.usec ())
		return true;
	return false;
}
inline
bool operator >= (const hy_timeval &tv1, const hy_timeval &tv2)
{
	if (tv1.sec () > tv2.sec ())
		return true;
	else if (tv1.sec () == tv2.sec ()
		&& tv1.usec () >= tv2.usec ())
		return true;
	return false;
}
inline
bool operator < (const hy_timeval &tv1, const hy_timeval &tv2)
{
	return tv2 > tv1;
}
inline
bool operator <= (const hy_timeval &tv1, const hy_timeval &tv2)
{
	return tv2 >= tv1;
}
inline
bool operator == (const hy_timeval &tv1, const hy_timeval &tv2)
{
	return tv1.sec () == tv2.sec ()
		&& tv1.usec () == tv2.usec ();
}
inline
bool operator != (const hy_timeval &tv1, const hy_timeval &tv2)
{
	return !(tv1 == tv2);
}
inline
hy_timeval operator + (const hy_timeval &tv1, const hy_timeval &tv2)
{
	hy_timeval sum (tv1);
	sum += tv2;
	return sum;
}
inline
hy_timeval operator - (const hy_timeval &tv1, const hy_timeval &tv2)
{
	hy_timeval sum (tv1);
	sum -= tv2;
	return sum;
}

//} //namespace hy 

#endif

